package Fishbowl_test;
import static org.junit.Assert.*;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;

import org.junit.Before;
import org.junit.Test;


public class Fishbowl_test {
	private int M, N;
	private int X, Y;
	private int RA, MA;
	private int RB, MB;
	private int seed;
	private int rounds;
	private String filename;
	
	/* 
	 * Testes de Sistema usando particionamento em classes 
	 * de equivalência e análise de valor limite 
	 */
	@Before
	public void setUp() {
		this.M = this.N = 5;
		this.X = this.Y = 2;
		this.RA = this.RB = this.MA = this.MB = 5;
		this.rounds = 5;
		this.filename = "dat.test";
		this.seed = 10;
	}
	@Test 
	public void testMLimit1() {
			this.M = 1;
			Fishbowl fishbowl = new Fishbowl(M, N, X, Y, RA, MA, RB, MB, seed);
			try {
				fishbowl.playGame(rounds, filename);
				assertEquals(" -  B  -  -  - \n(0, 1, 0, 3)\n5\n", fishbowl.toString());
			} catch (IOException e) {
				fail();
			}
	}
	
	@Test
	public void testMLimit0() {
			this.M = 0;
			Fishbowl fishbowl = null;
			try {
				fishbowl = new Fishbowl(M, N, X, Y, RA, MA, RB, MB, seed);
				fail();
			}
			catch(IllegalArgumentException i) {
				assertEquals("Invalid dimensions for fishbow", i.getMessage());
			}
	}
	
	@Test 
	public void testNLimit1() {
			this.N = 1;
			Fishbowl fishbowl = new Fishbowl(M, N, X, Y, RA, MA, RB, MB, seed);
			try {
				fishbowl.playGame(rounds, filename);
				assertEquals(" - \n - \n - \n B \n - \n(3, 0, 0, 1)\n5\n", fishbowl.toString());
			} catch (IOException e) {
				fail();
			}
	}
	
	@Test
	public void testNLimit0() {
			this.N = 0;
			Fishbowl fishbowl = null;
			try {
				fishbowl = new Fishbowl(M, N, X, Y, RA, MA, RB, MB, seed);
				fail();
			}
			catch(IllegalArgumentException i) {
				assertEquals("Invalid dimensions for fishbow", i.getMessage());
			}
	}
	
	@Test 
	public void testXNegative() {
			this.X = -1;
			Fishbowl fishbowl;
			try {
				fishbowl = new Fishbowl(M, N, X, Y, RA, MA, RB, MB, seed);
				fail();
			} catch(IllegalArgumentException i) {
				assertEquals("Invalid fish quantities", i.getMessage());
			}
	}
	
	@Test
	public void testXZero() {
			this.X = 0;
			Fishbowl fishbowl = null;
			try {
				fishbowl = new Fishbowl(M, N, X, Y, RA, MA, RB, MB, seed);
				fishbowl.playGame(rounds, filename);
				assertEquals(" -  -  -  -  - \n -  -  -  -  - \n -  -  -  -  - \n -  -  -  -  - \n -  -  -  -  - \n5\n", fishbowl.toString());
			}
			catch(IllegalArgumentException i) {
				assertEquals("Invalid dimensions for fishbow", i.getMessage());
			} catch (IOException e) {
				fail();
			}
	}
	
	@Test 
	public void testYNegative() {
			this.Y = -1;
			Fishbowl fishbowl;
			try {
				fishbowl = new Fishbowl(M, N, X, Y, RA, MA, RB, MB, seed);
				fail();
			} catch(IllegalArgumentException i) {
				assertEquals("Invalid fish quantities", i.getMessage());
			}
	}
	
	@Test
	public void testYZero() {
			this.Y = 0;
			Fishbowl fishbowl = null;
			fishbowl = new Fishbowl(M, N, X, Y, RA, MA, RB, MB, seed);
			try {
				fishbowl.playGame(rounds, filename);
			} catch (IOException e) {
				fail();
			}
			assertEquals(" -  -  -  -  - \n -  A  -  -  - \n -  -  -  -  - \n A  -  -  -  - \n -  -  -  -  - \n(3, 0, 0, 0)\n(1, 1, 0, 0)\n0\n", fishbowl.toString());
	}
	
	// ra, rb, ma, mb
	
	@Test
	public void testRAZero() {
		this.RA = 0;
		Fishbowl fishbowl = null;
		try {
			fishbowl = new Fishbowl(M, N, X, Y, RA, MA, RB, MB, seed);
		} catch(IllegalArgumentException i) {
			assertEquals("Invalid RA or RB", i.getMessage());
		}
	}
	@Test
	public void testRAPositive() {
		this.RA = 1;
		this.X = 1;
		this.Y = 0;
		Fishbowl fishbowl = null;
		
			fishbowl = new Fishbowl(M, N, X, Y, RA, MA, RB, MB, seed);
			assertEquals(1, fishbowl.getNumberOfFish(1));
			fishbowl.simulateOneStep();
			fishbowl.simulateOneStep();
			assertEquals(2, fishbowl.getNumberOfFish(1));
	}
	@Test
	public void testRBZero() {
		this.RB = 0;
		Fishbowl fishbowl = null;
		try {
			fishbowl = new Fishbowl(M, N, X, Y, RA, MA, RB, MB, seed);
		} catch(IllegalArgumentException i) {
			assertEquals("Invalid RA or RB", i.getMessage());
		}
	}
	@Test
	public void testRBPositive() {
		this.RB = 1;
		this.MB = 1000;
		this.RA = 1000;
		this.MA = 1000;
		this.M = 3;
		this.N = 3;
		this.X = 6;
		this.Y = 1;
		Fishbowl fishbowl = null;
		
		fishbowl = new Fishbowl(M, N, X, Y, RA, MA, RB, MB, seed);
		assertEquals(1, fishbowl.getNumberOfFish(-1));
		fishbowl.showFishbowl();
		fishbowl.simulateOneStep();
		fishbowl.showFishbowl();
		fishbowl.simulateOneStep();
		fishbowl.showFishbowl();
		assertEquals(2, fishbowl.getNumberOfFish(-1));
	}
	
	@Test
	public void testMAZero() {
		this.MA = 0;
		Fishbowl fishbowl = null;
		try {
			fishbowl = new Fishbowl(M, N, X, Y, RA, MA, RB, MB, seed);
		} catch(IllegalArgumentException i) {
			assertEquals("Invalid MA or MB", i.getMessage());
		}
	}
	
	@Test
	public void testMAPositive() {
		this.MA = 1;
		this.M = this.N = 1;
		this.X = 1;
		this.Y = 0;
		Fishbowl fishbowl = null;
		fishbowl = new Fishbowl(M, N, X, Y, RA, MA, RB, MB, seed);
		assertEquals(1, fishbowl.getNumberOfFish(1));
		fishbowl.simulateOneStep();
		fishbowl.simulateOneStep();
		assertEquals(0, fishbowl.getNumberOfFish(1));
	}
	
	@Test
	public void testMBZero() {
		this.MB = 0;
		Fishbowl fishbowl = null;
		try {
			fishbowl = new Fishbowl(M, N, X, Y, RA, MA, RB, MB, seed);
		} catch(IllegalArgumentException i) {
			assertEquals("Invalid MA or MB", i.getMessage());
		}
	}
	
	@Test
	public void testMBPositive() {
		this.MB = 1;
		this.X = 0;
		this.Y = 1;
		Fishbowl fishbowl = null;
		fishbowl = new Fishbowl(M, N, X, Y, RA, MA, RB, MB, seed);
		assertEquals(1, fishbowl.getNumberOfFish(-1));
		fishbowl.simulateOneStep();
		fishbowl.simulateOneStep();
		assertEquals(0, fishbowl.getNumberOfFish(-1));
	}
	
	/*
	 * Teste unitário das funções
	 */
	@Test
	public void testFishA() {
		int X = 10, Y = 10;
		Fish A = new FishA(X, Y);
		assertEquals(X, A.getX());
		assertEquals(Y, A.getY());
		assertEquals(0, A.getFood());
		assertEquals(0, A.getStarvation());
		assertEquals(true, A.isAlive());
		
		int food = 5, starvation = 3;
		A.setFood(food);
		A.setStarvation(starvation);
		assertEquals(food, A.getFood());
		assertEquals(starvation, A.getStarvation());
	}
	
	@Test
	public void testFishB() {
		int X = 10, Y = 10;
		Fish B = new FishB(X, Y);
		assertEquals(X, B.getX());
		assertEquals(Y, B.getY());
		assertEquals(0, B.getFood());
		assertEquals(0, B.getStarvation());
		assertEquals(true, B.isAlive());
		
		int food = 5, starvation = 3;
		B.setFood(food);
		B.setStarvation(starvation);
		assertEquals(food, B.getFood());
		assertEquals(starvation, B.getStarvation());
	}
	
	@Test
	public void testGetNumberOfFish() {
		this.X = 1;
		this.Y = 0;
		Fishbowl fishbowl = new Fishbowl(M, N, X, Y, RA, MA, RB, MB, seed);
		assertEquals(this.Y, fishbowl.getNumberOfFish(-1));
		assertEquals(this.X, fishbowl.getNumberOfFish(1));
		assertEquals(this.X + this.Y, fishbowl.getNumberOfFish(0));
	}
	
	@Test
	public void testCellManagement() {
		int option = 2;
		int cellX = 2, cellY = 2;
		Fishbowl fishbowl = new Fishbowl(M, N, X, Y, RA, MA, RB, MB, seed);
		fishbowl.setCell(cellX, cellY, option);
		assertEquals(option, fishbowl.getCell(cellX, cellY));
	}
	
	@Test
	public void testNeighborhood(){
		this.X = 1;
		this.Y = 0;
		this.M = 3;
		this.N = 3;
		
		Fishbowl fishbowl = new Fishbowl(M, N, X, Y, RA, MA, RB, MB, seed);
//	     Fishbowl configuration
//		 A  -  - 
//		 -  -  - 
//		 -  -  - 
		assertEquals(0, fishbowl.getNeighborhood(1, 1, -1).size());
		assertEquals(1, fishbowl.getNeighborhood(1, 1, 1).size());
		assertEquals(7, fishbowl.getNeighborhood(1, 1, 0).size());
		
		assertEquals(3, fishbowl.getNeighborhood(2, 2, 0).size());
		assertEquals(0, fishbowl.getNeighborhood(2, 2, 1).size());
		assertEquals(0, fishbowl.getNeighborhood(2, 2, -1).size());
	}
	
	@Test
	public void testSaveGame() {
		this.X = 1;
		this.Y = 0;
		Fishbowl fishbowl = new Fishbowl(M, N, X, Y, RA, MA, RB, MB, seed);
		try {
			fishbowl.saveGame(filename);
			fishbowl = new Fishbowl(filename);
			assertEquals(1, fishbowl.getNumberOfFish(1));
			assertEquals(0, fishbowl.getNumberOfFish(-1));
			assertEquals(1, fishbowl.getNumberOfFish(0));
		} catch (IOException e) {
			fail();
		}
	}
	
	@Test
	public void testFishbowlCapacity() {
		this.X = this.M * this.N;
		this.Y = 1;
		try{
			Fishbowl fishbowl = new Fishbowl(M, N, X, Y, RA, MA, RB, MB, seed);
			fail();
		} catch(IllegalArgumentException i) {
			assertEquals("Fishbow smaller than necessary", i.getMessage());
		}
	}
	
	@Test
	public void testLoadInvalidDim() {
		try{
			Fishbowl fishbowl = new Fishbowl("InvDim");
			fail();
		} catch(IllegalArgumentException i) {
			assertEquals("Invalid dimensions for fishbow", i.getMessage());
		} catch (FileNotFoundException e) {
			fail();
		}
	}
	
	@Test
	public void testLoadInvalidFishQuant() {
		try{
			Fishbowl fishbowl = new Fishbowl("InvFishQuant");
			fail();
		} catch(IllegalArgumentException i) {
			assertEquals("Invalid fish quantities", i.getMessage());
		} catch (FileNotFoundException e) {
			fail();
		}
	}
	
	@Test
	public void testLoadSmallerFishbowl() {
		try{
			Fishbowl fishbowl = new Fishbowl("SmallerFi");
			fail();
		} catch(IllegalArgumentException i) {
			assertEquals("Fishbow smaller than necessary", i.getMessage());
		} catch (FileNotFoundException e) {
			fail();
		}
	}
	
	@Test
	public void testInvalidFishbowl() {
		try{
			Fishbowl fishbowl = new Fishbowl("nonexistent");
			fail();
		} catch (FileNotFoundException e) {
			
		}
	}
	
	@Test
	public void testGetValue() {
		Fishbowl fishbowl = new Fishbowl(M, N, X, Y, RA, MA, RB, MB, seed);
		assertEquals('B', fishbowl.getValue(-1));
		assertEquals('A', fishbowl.getValue(1));
		assertEquals('-', fishbowl.getValue(0));		
	}
	
	@Test 
	public void testInvalidGetValue() {
		try {
			Fishbowl fishbowl = new Fishbowl(M, N, X, Y, RA, MA, RB, MB, seed);
			fishbowl.getValue(-2);
		}
		catch(IllegalArgumentException i) {
			assertEquals("Unknown element inside fishbowl", i.getMessage());
		}
	}
	
	@Test
	public void testFishBstarvation() {
		this.M = 3;
		this.N = 3;
		this.X = 0;
		this.Y = 1;
		this.MB = 1;
		Fishbowl fishbowl = new Fishbowl(M, N, X, Y, RA, MA, RB, MB, seed);
		try {
			fishbowl.playGame(-1, filename);
		} catch (IOException e) {
			fail();
		}
	}
	
	@Test
	public void testLoadFile() {
		try {
			Fishbowl fishbowl = new Fishbowl("valid_fishbowl");
		} catch (FileNotFoundException e) {
			fail();
		}
	}
}
